package org.ourgrid.broker.business.scheduler.workqueue.xmlcreator;

import java.util.Map;
import java.util.Map.Entry;

import org.ourgrid.broker.communication.operations.FileTransferOperation;
import org.ourgrid.broker.communication.operations.GetOperation;
import org.ourgrid.broker.communication.operations.InitOperation;
import org.ourgrid.common.interfaces.to.GridProcessAccounting;
import org.ourgrid.common.interfaces.to.GridProcessPhasesData;
import org.ourgrid.common.interfaces.to.GridProcessResultInfo;
import org.ourgrid.common.interfaces.to.RequestSpec;
import org.ourgrid.common.interfaces.to.TransferTime;
import org.ourgrid.peer.to.PeerBalance;

import br.edu.ufcg.lsd.commune.processor.filetransfer.IncomingTransferHandle;
import br.edu.ufcg.lsd.commune.processor.filetransfer.OutgoingTransferHandle;
import br.edu.ufcg.lsd.commune.processor.filetransfer.TransferHandle;
import br.edu.ufcg.lsd.commune.processor.filetransfer.TransferProgress;


public class ReportReplicaAccountingXMLCreator implements XMLCreatorIF {
	
	
	/**
	 * <REPORT_ACCOUNTING peerID='String' workerID='String' state='String' initCPUTime='long'
	 *                    requestID='long' taskSequenceNumber='int' processSequenceNumber='int' creationTime='long'
	 *                    latestPhase='String' sabotageCheck='String' jobID='int' requiredWorkers='int'
	 *         			  maxFails='int' maxReplicas='int'/>
	 *                    
	 *      <PHASES_DATA initBeginning='long' initEnd='long' remoteBeginning='long' remoteEnd='long'
	 *                   finalBeginning='long' finalEnd='long'>
	 *                   
	 *            1..n <INIT_OPERATION jobID='int' taskID='int' processID='int' requestID='long' 
	 *                                 workerID='String' localFilePath='String' remoteFilePath='String' 
	 *                                 transferDescription='String' initTime='long' endTime='long' fileSize='long' />
	 *                                 
	 *            1..n <GET_OPERATION jobID='int' taskID='int' processID='int' requestID='long' 
	 *                                 workerID='String' localFilePath='String' remoteFilePath='String' 
	 *                                 transferDescription='String' initTime='long' endTime='long' fileSize='long' />
	 *                    
	 *      </PHASES_DATA>              
	 * 
	 *      <PROCESS_RESULT_INFO exitValue='int' errorCause='String' executionErrorType='String'
	 *                           stderr='String' stdout='String'/>
	 *                           
	 * 1..n <PEER_BALANCE attribute='String' value='String' />
	 *                     
	 * 1..n	<TRANSFER_PROGRESS handleID='long' localFileName='String' destinationFileName='String' 
	 *                         fileSize='long' description='String'
	 *                         id='String' newStatus='String' amountWritten='long' progress='double'
	 *                         transferRate='double' outgoing='boolean'/>
	 * 
	 * </REPORT_ACCOUNTING>
	 */
	public String getXML(String peerID, GridProcessAccounting accouting) {
		
		RequestSpec spec = accouting.getRequestSpec();
		long jobID = spec.getJobId();
		int requiredWorkers = spec.getRequiredWorkers();
		int maxFails = spec.getMaxFails();
		int maxReplicas = spec.getMaxReplicas();
		
		StringBuffer buffer = new StringBuffer();
		
		buffer.append("<REPORT_ACCOUNTING ");
		buffer.append("peerID='" + peerID + "' ");
		buffer.append("workerID='" + accouting.getWorkerID() + "' ");
		buffer.append("state='" + accouting.getState() + "' ");
		buffer.append("initCPUTime='" + accouting.getInitCPUTime() + "' ");
		buffer.append("requestID='" + accouting.getRequestSpec().getRequestId() + "' ");
		buffer.append("taskSequenceNumber='" + accouting.getTaskSequenceNumber() + "' ");
		buffer.append("processSequenceNumber='" + accouting.getGridProcessSequenceNumber() + "' ");
		buffer.append("creationTime='" + accouting.getCreationTime() + "' ");
		buffer.append("latestPhase='" + accouting.getLatestPhase() + "' ");
		buffer.append("sabotageCheck='" + accouting.getSabotageCheck() + "' ");
		buffer.append("jobID='" + jobID + "' ");
		buffer.append("requiredWorkers='" + requiredWorkers + "' ");
		buffer.append("maxFails='" + maxFails + "' ");
		buffer.append("maxReplicas='" + maxReplicas + "'> ");
		buffer.append(SEP);
		buffer.append(SEP);
		
		GridProcessPhasesData phasesData = accouting.getPhasesData();

		if (phasesData != null) {
			buffer.append("<PHASES_DATA ");
			buffer.append("initBeginning='" + phasesData.getInitBeginning() + "' ");
			buffer.append("initEnd='" + phasesData.getInitEnd() + "' ");
			buffer.append("remoteBeginning='" + phasesData.getRemoteBeginning() + "' ");
			buffer.append("remoteEnd='" + phasesData.getRemoteEnd() + "' ");
			buffer.append("finalBeginning='" + phasesData.getFinalBeginning() + "' ");
			buffer.append("finalEnd='" + phasesData.getFinalEnd() + "'> ");
			buffer.append(SEP);
			buffer.append(SEP);
		}
		
		Map<InitOperation, TransferTime> initOperations = phasesData.getInitOperations();
		
		if (initOperations != null) {
			
			InitOperation operation = null;
			TransferTime time = null;
			for (Entry<InitOperation, TransferTime> entry : initOperations.entrySet()) {
				operation = entry.getKey();
				time = entry.getValue();
				buffer.append(getOperationXml(false, operation, time));			}
		}
		
		Map<GetOperation, TransferTime> getOperations = phasesData.getGetOperations();
		
		if (getOperations != null) {
			
			GetOperation operation = null;
			TransferTime time = null;
			for (Entry<GetOperation, TransferTime> entry : getOperations.entrySet()) {
				operation = entry.getKey();
				time = entry.getValue();
				buffer.append(getOperationXml(true, operation, time));
			}
		}
		
		buffer.append("</PHASES_DATA>");
		buffer.append(SEP);
		buffer.append(SEP);
		
		GridProcessResultInfo resultInfo = accouting.getResultInfo();
		
		String errorCause = "";
		String executionErrorType = "";
		String stderr = "";
		String stdout = "";
		Integer exitValue = -1;
		
		if (resultInfo.getExitValue() != null) {
			exitValue = resultInfo.getExitValue();
		}
		
		if (resultInfo.getErrorCause() != null) {
			errorCause = resultInfo.getErrorCause();
		}
		
		if (resultInfo.getExecutionErrorType() != null) {
			executionErrorType = resultInfo.getExecutionErrorType();
		}
		
		if (resultInfo.getStderr() != null) {
			stderr = resultInfo.getStderr();
		}
		
		if (resultInfo.getStdout() != null) {
			stdout = resultInfo.getStdout();
		}
		
		if (resultInfo != null) {
			
			errorCause = errorCause.replace('"', '-').replace('\'', '-');
			stderr = stderr.replace('"', '-').replace('\'', '-');
			stdout = stdout.replace('"', '-').replace('\'', '-');
			
			buffer.append("<PROCESS_RESULT_INFO ");
			buffer.append("exitValue='" + exitValue + "' ");
			buffer.append("errorCause='" + errorCause + "' ");
			buffer.append("executionErrorType='" + executionErrorType + "' >");

			buffer.append("<stderr> <![CDATA[" + toValidXML(stderr) + "]]> </stderr>");
			buffer.append("<stdout> <![CDATA[" + toValidXML(stdout) + "]]> </stdout> ");

			buffer.append("</PROCESS_RESULT_INFO>");
			buffer.append(SEP);
			buffer.append(SEP);
		}	
		
		PeerBalance balance = accouting.getAccountings();
		
		if (balance != null) {
			for (Entry<String, Double> entry : balance.getBalances().entrySet()) {
				buffer.append("<PEER_BALANCE ");
				buffer.append("attribute='" + entry.getKey() + "' ");
				buffer.append("value='" + entry.getValue() + "'/> ");
				buffer.append(SEP);
				buffer.append(SEP);
			}
		}
		
		Map<TransferHandle, TransferProgress> tProgress = accouting.getTransfersProgress();
		
		if (tProgress != null) {
			TransferHandle handle = null;
			TransferProgress progress = null;
			long fileSize = 0;
			String id = null;
			
			for (Entry<TransferHandle, TransferProgress> entry : tProgress.entrySet()) {
				
				handle = entry.getKey();
				progress = entry.getValue();
				
				if (progress.isOutgoing()) {
					id = ((OutgoingTransferHandle) handle).getDestinationID().toString();
				} else {
					fileSize = ((IncomingTransferHandle) handle).getFileSize();
					id = ((IncomingTransferHandle) handle).getSenderID().toString();
				}
				
				buffer.append("<TRANSFER_PROGRESS ");
				buffer.append("handleID='" + handle.getId() + "' ");
				buffer.append("localFileName='" + handle.getLogicalFileName() + "' ");
				buffer.append("fileSize='" + fileSize + "' ");
				buffer.append("description='" + handle.getDescription() + "' ");
				buffer.append("id='" + id + "' ");
				buffer.append("newStatus='" + progress.getNewStatus() + "' ");
				buffer.append("amountWritten='" + progress.getAmountWritten() + "' ");
				buffer.append("progress='" + progress.getProgress() + "' ");
				buffer.append("transferRate='" + progress.getTransferRate() + "' ");
				buffer.append("outgoing='" + progress.isOutgoing() + "' /> ");
				buffer.append(SEP);
				buffer.append(SEP);
			}
		}
		
		buffer.append("</REPORT_ACCOUNTING>");
		
		return buffer.toString();
	}
	
	private String toValidXML(String string) {
		
		StringBuilder builder = new StringBuilder();
		for (char current : string.toCharArray()) {
			if ((current == 0x9) ||
	                (current == 0xA) ||
	                (current == 0xD) ||
	                ((current >= 0x20) && (current <= 0xD7FF)) ||
	                ((current >= 0xE000) && (current <= 0xFFFD)) ||
	                ((current >= 0x10000) && (current <= 0x10FFFF))) {
				builder.append(current);
			}
		}
		
		return builder.toString();
	}
	
	private String getOperationXml(boolean get, FileTransferOperation operation,
			TransferTime time) {
		
		StringBuffer buffer = new StringBuffer();
				
		if (get) {
			buffer.append("<GET_OPERATION ");
		} else {
			buffer.append("<INIT_OPERATION ");
		}
		
		
		buffer.append("jobID='" + operation.getGridProcessHandle().getJobID() + "' ");
		buffer.append("taskID='" + operation.getGridProcessHandle().getTaskID() + "' ");
		buffer.append("processID='" + operation.getGridProcessHandle().getReplicaID() + "' ");
		buffer.append("requestID='" + operation.getRequestID() + "' ");
		buffer.append("workerID='" + operation.getWorkerID() + "' ");
		buffer.append("localFilePath='" + operation.getLocalFilePath() + "' ");
		buffer.append("remoteFilePath='" + operation.getRemoteFilePath() + "' ");
		buffer.append("transferDescription='" + operation.getHandle().getDescription() + "' ");
		
		buffer.append("fileSize='" + operation.getHandle().getFileSize() + "' ");
		buffer.append("initTime='" + time.getInitTime() + "' ");
		buffer.append("endTime='" + time.getEndTime() + "'/> ");
		buffer.append(SEP);
		buffer.append(SEP);
		
		return buffer.toString();
	}

}
